package com.cspm.config;

import com.cspm.common.proxy.DynamicDataSource;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.mapping.VendorDatabaseIdProvider;
import org.apache.ibatis.session.AutoMappingBehavior;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.LocalCacheScope;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.type.JdbcType;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * Created by CHQIU on 2017-07-31.
 */
@Configuration
@EnableTransactionManagement
@MapperScan(basePackages = "com.cspm.gen.dao")
public class MyBatisConfig implements TransactionManagementConfigurer {
    @Autowired
    private Environment env;

    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return new DataSourceTransactionManager(dataSource(dataSourceWrite(), dataSourceRead1(), dataSourceRead2()));
    }

    /**
     * DynamicDataSource 读写分离
     *
     * @Primary 该注解表示在同一个接口有多个实现类可以注入的时候，默认选择哪一个，而不是让@autowire注解报错
     * @Qualifier 根据名称进行注入，通常是在具有相同的多个类型的实例的一个注入（例如有多个DataSource类型的实例）
     */
    @Bean
    @Primary
    public DataSource dataSource(@Qualifier("dataSourceWrite") DataSource dataSourceWrite,
                                 @Qualifier("dataSourceRead1") DataSource dataSourceRead1,
                                 @Qualifier("dataSourceRead2") DataSource dataSourceRead2) {
        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setWriteDataSource(dataSourceWrite);
        List<Object> dataSourceReads = new ArrayList<>();
        dataSourceReads.add(dataSourceRead1);
        dataSourceReads.add(dataSourceRead2);
        dataSource.setReadDataSources(dataSourceReads);
        dataSource.setReadDataSourcePollPattern(1);//多数据源轮询方式
        dataSource.setDefaultTargetDataSource(dataSourceWrite);
        return dataSource;
    }

    @Bean(name = "dataSourceWrite")
    @ConfigurationProperties(prefix = "write.jdbc") // application.properteis中对应属性的前缀
    public DataSource dataSourceWrite() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "dataSourceRead1")
    @ConfigurationProperties(prefix = "read.jdbc") // application.properteis中对应属性的前缀
    public DataSource dataSourceRead1() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "dataSourceRead2")
    @ConfigurationProperties(prefix = "read2.jdbc") // application.properteis中对应属性的前缀
    public DataSource dataSourceRead2() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactoryBean() {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource(dataSourceWrite(), dataSourceRead1(), dataSourceRead2()));
        //添加插件
        //  bean.setPlugins(new Interceptor[]{ });
        //添加XML目录
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();

        org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
        // 使全局的映射器启用或禁用缓存
        config.setCacheEnabled(true);
        // 全局启用或禁用延迟加载。当禁用时，所有关联对象都会即时加载
        config.setLazyLoadingEnabled(true);
        // 当启用时，有延迟加载属性的对象在被调用时将会完全加载任意属性。否则，每种属性将会按需要加载。
        config.setAggressiveLazyLoading(true);
        // 是否允许单条sql 返回多个数据集  (取决于驱动的兼容性) default:true
        config.setMultipleResultSetsEnabled(true);
        // 是否可以使用列的别名 (取决于驱动的兼容性) default:true
        config.setUseColumnLabel(true);
        // 允许JDBC 生成主键。需要驱动器支持。如果设为了true，这个设置将强制使用被生成的主键，有一些驱动器不兼容不过仍然可以执行。
        config.setUseGeneratedKeys(true);
        // 指定 MyBatis 如何自动映射 数据基表的列 NONE：不隐射　PARTIAL:部分  FULL:全部
        config.setAutoMappingBehavior(AutoMappingBehavior.PARTIAL);
        // 这是默认的执行类型  （SIMPLE: 简单； REUSE: 执行器可能重复使用prepared statements语句；BATCH: 执行器可以重复执行语句和批量更新）
        config.setDefaultExecutorType(ExecutorType.SIMPLE);
        // 使用驼峰命名法转换字段
        config.setMapUnderscoreToCamelCase(true);
        // 设置本地缓存范围 session:就会有数据的共享  statement:语句范围 (这样就不会有数据的共享 ) defalut:session
        config.setLocalCacheScope(LocalCacheScope.SESSION);
        // 设置但JDBC类型为空时,某些驱动程序 要指定值,default:OTHER，插入空值时不需要指定类型
        config.setJdbcTypeForNull(JdbcType.NULL);
        // 设置一个时限，以决定让驱动器等待数据库回应的多长时间为超时（单位：秒）
        config.setDefaultStatementTimeout(900);
        // 打印查询语句
        config.setLogImpl(org.apache.ibatis.logging.stdout.StdOutImpl.class);
        bean.setConfiguration(config);

        Properties p = new Properties();
        p.setProperty("MySQL", "mysql");
        p.setProperty("Oracle", "oracle");
        p.setProperty("SQL Server", "sqlserver");
        p.setProperty("DB2", "db2");
        DatabaseIdProvider databaseIdProvider = new VendorDatabaseIdProvider();
        databaseIdProvider.setProperties(p);
        bean.setDatabaseIdProvider(databaseIdProvider);

        try {
            //自动扫描mapper.xml文件
            bean.setMapperLocations(resolver.getResources("classpath*:mapper/*Mapper.xml"));
            return bean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}
